home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 7 / Amiga Format AFCD07 (Dec 1996, Issue 91).iso / serious / shareware / programming / emacs-complete / fsf / emacs / lib-src / fakemail.c < prev    next >
C/C++ Source or Header  |  1994-05-03  |  15KB  |  673 lines

  1. /* sendmail-like interface to /bin/mail for system V,
  2.    Copyright (C) 1985, 1994 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU Emacs.
  5.  
  6. GNU Emacs is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. GNU Emacs is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU Emacs; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20.  
  21. #define NO_SHORTNAMES
  22. #include <../src/config.h>
  23.  
  24. #if defined (BSD) && !defined (BSD4_1) && !defined (USE_FAKEMAIL)
  25. /* This program isnot used in BSD, so just avoid loader complaints.  */
  26. main ()
  27. {
  28. }
  29. #else /* not BSD 4.2 (or newer) */
  30. #ifdef MSDOS
  31. main ()
  32. {
  33. }
  34. #else /* not MSDOS */
  35. /* This conditional contains all the rest of the file.  */
  36.  
  37. /* These are defined in config in some versions. */
  38.  
  39. #ifdef static
  40. #undef static
  41. #endif
  42.  
  43. #ifdef read
  44. #undef read
  45. #undef write
  46. #undef open
  47. #undef close
  48. #endif
  49.  
  50. #include <stdio.h>
  51. #include <string.h>
  52. #include <ctype.h>
  53. #include <time.h>
  54. #include <pwd.h>
  55.  
  56. /* Type definitions */
  57.  
  58. #define boolean int
  59. #define true 1
  60. #define false 0
  61.  
  62. /* Various lists */
  63.  
  64. struct line_record
  65. {
  66.   char *string;
  67.   struct line_record *continuation;
  68. };
  69. typedef struct line_record *line_list;
  70.  
  71. struct header_record
  72. {
  73.   line_list text;
  74.   struct header_record *next;
  75.   struct header_record *previous;
  76. };
  77. typedef struct header_record *header;
  78.             
  79. struct stream_record
  80. {
  81.   FILE *handle;
  82.   int (*action)();
  83.   struct stream_record *rest_streams;
  84. };
  85. typedef struct stream_record *stream_list;
  86.  
  87. /* A `struct linebuffer' is a structure which holds a line of text.
  88.  * `readline' reads a line from a stream into a linebuffer
  89.  * and works regardless of the length of the line.
  90.  */
  91.  
  92. struct linebuffer
  93. {
  94.   long size;
  95.   char *buffer;
  96. };
  97.  
  98. struct linebuffer lb;
  99.  
  100. #define new_list()                    \
  101.   ((line_list) xmalloc (sizeof (struct line_record)))
  102. #define new_header()                \
  103.   ((header) xmalloc (sizeof (struct header_record)))
  104. #define new_stream()                \
  105.   ((stream_list) xmalloc (sizeof (struct stream_record)))
  106. #define alloc_string(nchars)                \
  107.   ((char *) xmalloc ((nchars) + 1))
  108.  
  109. /* Global declarations */
  110.  
  111. #define BUFLEN 1024
  112. #define KEYWORD_SIZE 256
  113. #define FROM_PREFIX "From"
  114. #define MY_NAME "fakemail"
  115. #define NIL ((line_list) NULL)
  116. #define INITIAL_LINE_SIZE 200
  117.  
  118. #ifndef MAIL_PROGRAM_NAME
  119. #define MAIL_PROGRAM_NAME "/bin/mail"
  120. #endif
  121.  
  122. static char *my_name;
  123. static char *the_date;
  124. static char *the_user;
  125. static line_list file_preface;
  126. static stream_list the_streams;
  127. static boolean no_problems = true;
  128.  
  129. extern FILE *popen ();
  130. extern int fclose (), pclose ();
  131. extern char *malloc (), *realloc ();
  132.  
  133. #ifdef CURRENT_USER
  134. extern struct passwd *getpwuid ();
  135. extern unsigned short geteuid ();
  136. static struct passwd *my_entry;
  137. #define cuserid(s)                \
  138. (my_entry = getpwuid (((int) geteuid ())),    \
  139.  my_entry->pw_name)
  140. #endif
  141.  
  142. /* Utilities */
  143.  
  144. /* Print error message.  `s1' is printf control string, `s2' is arg for it. */
  145.  
  146. static void
  147. error (s1, s2)
  148.      char *s1, *s2;
  149. {
  150.   printf ("%s: ", my_name);
  151.   printf (s1, s2);
  152.   printf ("\n");
  153.   no_problems = false;
  154. }
  155.  
  156. /* Print error message and exit.  */
  157.  
  158. static void
  159. fatal (s1, s2)
  160.      char *s1, *s2;
  161. {
  162.   error (s1, s2);
  163.   exit (1);
  164. }
  165.  
  166. /* Like malloc but get fatal error if memory is exhausted.  */
  167.  
  168. static char *
  169. xmalloc (size)
  170.      int size;
  171. {
  172.   char *result = malloc (((unsigned) size));
  173.   if (result == ((char *) NULL))
  174.     fatal ("virtual memory exhausted", 0);
  175.   return result;
  176. }
  177.  
  178. static char *
  179. xrealloc (ptr, size)
  180.      char *ptr;
  181.      int size;
  182. {
  183.   char *result = realloc (ptr, ((unsigned) size));
  184.   if (result == ((char *) NULL))
  185.     fatal ("virtual memory exhausted");
  186.   return result;
  187. }
  188.  
  189. /* Initialize a linebuffer for use */
  190.  
  191. void
  192. init_linebuffer (linebuffer)
  193.      struct linebuffer *linebuffer;
  194. {
  195.   linebuffer->size = INITIAL_LINE_SIZE;
  196.   linebuffer->buffer = ((char *) xmalloc (INITIAL_LINE_SIZE));
  197. }
  198.  
  199. /* Read a line of text from `stream' into `linebuffer'.
  200.  * Return the length of the line.  
  201.  */
  202.  
  203. long
  204. readline (linebuffer, stream)
  205.      struct linebuffer *linebuffer;
  206.      FILE *stream;
  207. {
  208.   char *buffer = linebuffer->buffer;
  209.   char *p = linebuffer->buffer;
  210.   char *end = p + linebuffer->size;
  211.  
  212.   while (true)
  213.     {
  214.       int c = getc (stream);
  215.       if (p == end)
  216.     {
  217.       linebuffer->size *= 2;
  218.       buffer = ((char *) xrealloc (buffer, linebuffer->size));
  219.       p = buffer + (p - linebuffer->buffer);
  220.       end = buffer + linebuffer->size;
  221.       linebuffer->buffer = buffer;
  222.     }
  223.       if (c < 0 || c == '\n')
  224.     {
  225.       *p = 0;
  226.       break;
  227.     }
  228.       *p++ = c;
  229.     }
  230.  
  231.   return p - buffer;
  232. }
  233.  
  234. char *
  235. get_keyword (field, rest)
  236.      register char *field;
  237.      char **rest;
  238. {
  239.   static char keyword[KEYWORD_SIZE];
  240.   register char *ptr;
  241.   register char c;
  242.  
  243.   ptr = &keyword[0];
  244.   c = *field++;
  245.   if ((isspace (c)) || (c == ':'))
  246.     return ((char *) NULL);
  247.   *ptr++ = ((islower (c)) ? (toupper (c)) : c);
  248.   while (((c = *field++) != ':') && (!(isspace (c))))
  249.     *ptr++ = ((islower (c)) ? (toupper (c)) : c);
  250.   *ptr++ = '\0';
  251.   while (isspace (c)) c = *field++;
  252.   if (c != ':') return ((char *) NULL);
  253.   *rest = field;
  254.   return &keyword[0];
  255. }
  256.  
  257. boolean
  258. has_keyword (field)
  259.      char *field;
  260. {
  261.   char *ignored;
  262.   return (get_keyword (field, &ignored) != ((char *) NULL));
  263. }
  264.  
  265. char *
  266. add_field (the_list, field, where)
  267.      line_list the_list;
  268.      register char *field, *where;
  269. {
  270.   register char c;
  271.   while (true)
  272.     {
  273.       *where++ = ' ';
  274.       while ((c = *field++) != '\0')
  275.     {
  276.       if (c == '(')
  277.         {
  278.           while (*field && *field != ')') ++field;
  279.           if (! (*field++)) break; /* no closer */
  280.           if (! (*field))   break; /* closerNULL */
  281.           c = *field;
  282.         }
  283.       *where++ = ((c == ','||c=='>'||c=='<') ? ' ' : c);
  284.     }
  285.       if (the_list == NIL) break;
  286.       field = the_list->string;
  287.       the_list = the_list->continuation;
  288.     }
  289.   return where;
  290. }
  291.  
  292. line_list
  293. make_file_preface ()
  294. {
  295.   char *the_string, *temp;
  296.   long idiotic_interface;
  297.   long prefix_length;
  298.   long user_length;
  299.   long date_length;
  300.   line_list result;
  301.  
  302.   prefix_length = strlen (FROM_PREFIX);
  303.   time (&idiotic_interface);
  304.   the_date = ctime (&idiotic_interface);
  305.   /* the_date has an unwanted newline at the end */
  306.   date_length = strlen (the_date) - 1;
  307.   the_date[date_length] = '\0';
  308.   temp = cuserid ((char *) NULL);
  309.   user_length = strlen (temp);
  310.   the_user = alloc_string (user_length + 1);
  311.   strcpy (the_user, temp);
  312.   the_string = alloc_string (3 + prefix_length +
  313.                  user_length +
  314.                  date_length);
  315.   temp = the_string;
  316.   strcpy (temp, FROM_PREFIX);
  317.   temp = &temp[prefix_length];
  318.   *temp++ = ' ';
  319.   strcpy (temp, the_user);
  320.   temp = &temp[user_length];
  321.   *temp++ = ' ';
  322.   strcpy (temp, the_date);
  323.   result = new_list ();
  324.   result->string = the_string;
  325.   result->continuation = ((line_list) NULL);
  326.   return result;
  327. }
  328.  
  329. void
  330. write_line_list (the_list, the_stream)
  331.      register line_list the_list;
  332.      FILE *the_stream;
  333. {
  334.   for ( ;
  335.       the_list != ((line_list) NULL) ;
  336.       the_list = the_list->continuation)
  337.     {
  338.       fputs (the_list->string, the_stream);
  339.       putc ('\n', the_stream);
  340.     }
  341.   return;
  342. }
  343.  
  344. int
  345. close_the_streams ()
  346. {
  347.   register stream_list rem;
  348.   for (rem = the_streams;
  349.        rem != ((stream_list) NULL);
  350.        rem = rem->rest_streams)
  351.     no_problems = (no_problems &&
  352.            ((*rem->action) (rem->handle) == 0));
  353.   the_streams = ((stream_list) NULL);
  354.   return (no_problems ? 0 : 1);
  355. }
  356.  
  357. void
  358. add_a_stream (the_stream, closing_action)
  359.      FILE *the_stream;
  360.      int (*closing_action)();
  361. {
  362.   stream_list old = the_streams;
  363.   the_streams = new_stream ();
  364.   the_streams->handle = the_stream;
  365.   the_streams->action = closing_action;
  366.   the_streams->rest_streams = old;
  367.   return;
  368. }
  369.  
  370. int
  371. my_fclose (the_file)
  372.      FILE *the_file;
  373. {
  374.   putc ('\n', the_file);
  375.   fflush (the_file);
  376.   return fclose (the_file);
  377. }
  378.  
  379. boolean
  380. open_a_file (name)
  381.      char *name;
  382. {
  383.   FILE *the_stream = fopen (name, "a");
  384.   if (the_stream != ((FILE *) NULL))
  385.     {
  386.       add_a_stream (the_stream, my_fclose);
  387.       if (the_user == ((char *) NULL))
  388.     file_preface = make_file_preface ();
  389.       write_line_list (file_preface, the_stream);
  390.       return true;
  391.     }
  392.   return false;
  393. }
  394.  
  395. void
  396. put_string (s)
  397.      char *s;
  398. {
  399.   register stream_list rem;
  400.   for (rem = the_streams;
  401.        rem != ((stream_list) NULL);
  402.        rem = rem->rest_streams)
  403.     fputs (s, rem->handle);
  404.   return;
  405. }
  406.  
  407. void
  408. put_line (string)
  409.      char *string;
  410. {
  411.   register stream_list rem;
  412.   for (rem = the_streams;
  413.        rem != ((stream_list) NULL);
  414.        rem = rem->rest_streams)
  415.     {
  416.       char *s = string;
  417.       int column = 0;
  418.  
  419.       /* Divide STRING into lines.  */
  420.       while (*s != 0)
  421.     {
  422.       char *breakpos;
  423.  
  424.       /* Find the last char that fits.  */
  425.       for (breakpos = s; *breakpos && column < 78; ++breakpos)
  426.         {
  427.           if (*breakpos == '\t')
  428.         column += 8;
  429.           else
  430.         column++;
  431.         }
  432.       /* If we didn't reach end of line, break the line.  */
  433.       if (*breakpos)
  434.         {
  435.           /* Back up to just after the last comma that fits.  */
  436.           while (breakpos != s && breakpos[-1] != ',') --breakpos;
  437.  
  438.           if (breakpos == s)
  439.         {
  440.           /* If no comma fits, move past the first address anyway.  */
  441.           while (*breakpos != 0 && *breakpos != ',') ++breakpos;
  442.           if (*breakpos != 0)
  443.             /* Include the comma after it.  */
  444.             ++breakpos;
  445.         }
  446.         }
  447.       /* Output that much, then break the line.  */
  448.       fwrite (s, 1, breakpos - s, rem->handle);
  449.       column = 8;
  450.  
  451.       /* Skip whitespace and prepare to print more addresses.  */
  452.       s = breakpos;
  453.       while (*s == ' ' || *s == '\t') ++s;
  454.           if (*s != 0)
  455.         fputs ("\n\t", rem->handle);
  456.     }
  457.       putc ('\n', rem->handle);
  458.     }
  459.   return;
  460. }
  461.  
  462. #define mail_error error
  463.  
  464. void
  465. setup_files (the_list, field)
  466.      register line_list the_list;
  467.      register char *field;
  468. {
  469.   register char *start;
  470.   register char c;
  471.   while (true)
  472.     {
  473.       while (((c = *field) != '\0') &&
  474.          ((c == ' ') ||
  475.           (c == '\t') ||
  476.           (c == ',')))
  477.     field += 1;
  478.       if (c != '\0')
  479.     {
  480.       start = field;
  481.       while (((c = *field) != '\0') &&
  482.          (c != ' ') &&
  483.          (c != '\t') &&
  484.          (c != ','))
  485.         field += 1;
  486.       *field = '\0';
  487.       if (!open_a_file (start))
  488.         mail_error ("Could not open file %s", start);
  489.       *field = c;
  490.       if (c != '\0') continue;
  491.     }
  492.       if (the_list == ((line_list) NULL)) return;
  493.       field = the_list->string;
  494.       the_list = the_list->continuation;
  495.     }
  496. }
  497.  
  498. int
  499. args_size (the_header)
  500.      header the_header;
  501. {
  502.   register header old = the_header;
  503.   register line_list rem;
  504.   register int size = 0;
  505.   do
  506.     {
  507.       char *field;
  508.       register char *keyword = get_keyword (the_header->text->string, &field);
  509.       if ((strcmp (keyword, "TO") == 0) ||
  510.       (strcmp (keyword, "CC") == 0) ||
  511.       (strcmp (keyword, "BCC") == 0))
  512.     {
  513.       size += 1 + strlen (field);
  514.       for (rem = the_header->text->continuation;
  515.            rem != NIL;
  516.            rem = rem->continuation)
  517.         size += 1 + strlen (rem->string);
  518.     }
  519.       the_header = the_header->next;
  520.     } while (the_header != old);
  521.   return size;
  522. }
  523.  
  524. parse_header (the_header, where)
  525.      header the_header;
  526.      register char *where;
  527. {
  528.   register header old = the_header;
  529.   do
  530.     {
  531.       char *field;
  532.       register char *keyword = get_keyword (the_header->text->string, &field);
  533.       if (strcmp (keyword, "TO") == 0)
  534.     where = add_field (the_header->text->continuation, field, where);
  535.       else if (strcmp (keyword, "CC") == 0)
  536.     where = add_field (the_header->text->continuation, field, where);
  537.       else if (strcmp (keyword, "BCC") == 0)
  538.     {
  539.       where = add_field (the_header->text->continuation, field, where);
  540.       the_header->previous->next = the_header->next;
  541.       the_header->next->previous = the_header->previous;
  542.     }
  543.       else if (strcmp (keyword, "FCC") == 0)
  544.     setup_files (the_header->text->continuation, field);
  545.       the_header = the_header->next;
  546.     } while (the_header != old);
  547.   *where = '\0';
  548.   return;
  549. }
  550.     
  551. header
  552. read_header ()
  553. {
  554.   register header the_header = ((header) NULL);
  555.   register line_list *next_line = ((line_list *) NULL);
  556.  
  557.   init_linebuffer (&lb);
  558.  
  559.   do
  560.     {
  561.       long length;
  562.       register char *line;
  563.  
  564.       readline (&lb, stdin);
  565.       line = lb.buffer;
  566.       length = strlen (line);
  567.       if (length == 0) break;
  568.  
  569.       if (has_keyword (line))
  570.     {
  571.       register header old = the_header;
  572.       the_header = new_header ();
  573.       if (old == ((header) NULL))
  574.         {
  575.           the_header->next = the_header;
  576.           the_header->previous = the_header;
  577.         }
  578.       else
  579.         {
  580.           the_header->previous = old;
  581.           the_header->next = old->next;
  582.           old->next = the_header;
  583.         }
  584.       next_line = &(the_header->text);
  585.     }
  586.  
  587.       if (next_line == ((line_list *) NULL))
  588.     {
  589.       /* Not a valid header */
  590.       exit (1);
  591.     }
  592.       *next_line = new_list ();
  593.       (*next_line)->string = alloc_string (length);
  594.       strcpy (((*next_line)->string), line);
  595.       next_line = &((*next_line)->continuation);
  596.       *next_line = NIL;
  597.  
  598.     } while (true);
  599.  
  600.   return the_header->next;
  601. }
  602.  
  603. void
  604. write_header (the_header)
  605.      header the_header;
  606. {
  607.   register header old = the_header;
  608.   do
  609.     {
  610.       register line_list the_list;
  611.       for (the_list = the_header->text;
  612.        the_list != NIL;
  613.        the_list = the_list->continuation)
  614.     put_line (the_list->string);
  615.       the_header = the_header->next;
  616.     } while (the_header != old);
  617.   put_line ("");
  618.   return;
  619. }
  620.  
  621. void
  622. main (argc, argv)
  623.      int argc;
  624.      char **argv;
  625. {
  626.   char *command_line;
  627.   header the_header;
  628.   long name_length;
  629.   char *mail_program_name;
  630.   char buf[BUFLEN + 1];
  631.   register int size;
  632.   FILE *the_pipe;
  633.  
  634.   extern char *getenv ();
  635.  
  636.   mail_program_name = getenv ("FAKEMAILER");
  637.   if (!(mail_program_name && *mail_program_name))
  638.     mail_program_name = MAIL_PROGRAM_NAME;
  639.   name_length = strlen (mail_program_name);
  640.  
  641.   my_name = MY_NAME;
  642.   the_streams = ((stream_list) NULL);
  643.   the_date = ((char *) NULL);
  644.   the_user = ((char *) NULL);
  645.  
  646.   the_header = read_header ();
  647.   command_line = alloc_string (name_length + args_size (the_header));
  648.   strcpy (command_line, mail_program_name);
  649.   parse_header (the_header, &command_line[name_length]);
  650.   
  651.   the_pipe = popen (command_line, "w");
  652.   if (the_pipe == ((FILE *) NULL))
  653.     fatal ("cannot open pipe to real mailer");
  654.  
  655.   add_a_stream (the_pipe, pclose);
  656.  
  657.   write_header (the_header);
  658.  
  659.   /* Dump the message itself */
  660.  
  661.   while (!feof (stdin))
  662.     {
  663.       size = fread (buf, 1, BUFLEN, stdin);
  664.       buf[size] = '\0';
  665.       put_string (buf);
  666.     }
  667.  
  668.   exit (close_the_streams ());
  669. }
  670.  
  671. #endif /* not MSDOS */
  672. #endif /* not BSD 4.2 (or newer) */
  673.